//
//  AudioController.swift
//  hello
//
//  Created by 赵康 on 2024/11/11.
//

import Fluent
import Redis
import Vapor

struct AudioController: RouteCollection {
  func boot(routes: any RoutesBuilder) throws {
    let audios = routes.grouped("\(RouteEnum.audios.rawValue)")

    audios.post("\(RouteEnum.create.rawValue)", use: create)
    let protected = audios.grouped(AuthMiddleware())

    protected
      .get(
        "\(RouteEnum.fetch.rawValue)",
        ":albumID",
        use: getAudiosByAlbum
      )
    protected.get("\(RouteEnum.histories.rawValue)", "batch", use: fetchMultipleAudiosForPlaylist)
    protected
      .get(
        "\(RouteEnum.audioAndAlbumcover.rawValue)",
        ":audioID",
        use: getAudioCovers
      )
    
    // Add public endpoint for audio details without authentication
    audios.get("public", ":audioID", use: getPublicAudioDetails)
  }
  
  // New public endpoint for audio details
  @Sendable
  func getPublicAudioDetails(req: Request) async throws -> AudioPublicResponse {
    LogManager.shared.info("开始处理公共音频详情请求", context: "audioID: \(req.parameters.get("audioID", as: String.self) ?? "未知")")
    
    guard let audioID = req.parameters.get("audioID", as: UUID.self) else {
      LogManager.shared.error("无效的音频ID格式", context: "参数值: \(req.parameters.get("audioID") ?? "空")")
      throw Abort(.badRequest, reason: "Invalid audio ID")
    }

    // 检查是否是系统音频
    let systemAudioID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!
    if audioID == systemAudioID {
      LogManager.shared.warning("尝试访问系统音频", context: "audioID: \(audioID)")
      throw Abort(.notFound, reason: "Audio not found or has been deleted")
    }
    
    // 构建缓存键
    let cacheKey = CacheConfig.KeyBuilder.publicAudioDetails(audioID: audioID)
    
    // 尝试从缓存获取
    if let cachedResponse = try? await req.cache.get(
      forKey: cacheKey,
      as: AudioPublicResponse.self,
      from: req.redis
    ) {
      LogManager.shared.debug("命中缓存", context: "audioID: \(audioID)")
      return cachedResponse
    }

    // 查询音频信息，包含专辑信息
    guard let audio = try await Audio.query(on: req.db)
      .filter(\.$id == audioID)
      .filter(\.$isDeleted == false)
      .with(\.$album)
      .first() else {
      LogManager.shared.warning("音频不存在或已删除", context: "audioID: \(audioID)")
      throw Abort(.notFound, reason: "Audio not found or has been deleted")
    }

    // 构建响应
    let response = AudioPublicResponse(
      id: audio.id!,
      title: audio.title,
      description: audio.description,
      duration: audio.duration,
      cover: audio.cover,
      author: audio.author,
      url: audio.url,
      size: audio.size,
      albumID: audio.album.id!,
      albumTitle: audio.album.title,
      albumCover: audio.album.cover
    )

    // 缓存响应
    do {
      try await req.cache.set(
        response,
        forKey: cacheKey,
        duration: CacheConfig.Duration.medium,
        type: .audio,
        on: req.redis
      )
      LogManager.shared.info("成功缓存公共音频详情", context: "audioID: \(audioID)")
    } catch {
      LogManager.shared.error("缓存公共音频详情失败", context: "\(error)")
    }

    return response
  }
  
  @Sendable
  func fetchMultipleAudiosForPlaylist(req: Request) async throws -> [HistoryResponse] {
    let userID = try req.authenticatedUserID
    // 从查询参数获取音频ID列表
    guard let audioIdsString = req.query[String.self, at: "audioIds"],
      !audioIdsString.isEmpty
    else {
      throw Abort(.badRequest, reason: "Missing audio IDs")
    }

    // 2. 打印分割后的字符串数组
    let splitStrings = audioIdsString.split(separator: ",")
    // 解析音频ID
    let audioIds = splitStrings.compactMap { idString -> UUID? in
      let trimmed = String(idString).trimmingCharacters(in: .whitespacesAndNewlines)
      if let uuid = UUID(String(trimmed)) {
        return uuid
      } else {
        return nil
      }
    }
    if audioIds.isEmpty {
      throw Abort(.badRequest, reason: "Invalid audio IDs format")
    }

    // 构建缓存键
    let cacheKey = CacheConfig.KeyBuilder.audioHistories(
      audioIds: audioIds,
      userID: userID
    )

    // 尝试从缓存获取
    if let cachedResponses = try await req.cache.get(
      forKey: cacheKey,
      as: [HistoryResponse].self,
      from: req.redis
    ) {
      if !cachedResponses.isEmpty {
        return cachedResponses
      }
    }

    // 系统音频ID
    let systemAudioID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!

    // 查询音频信息，过滤掉系统音频
    let audios = try await Audio.query(on: req.db)
      .filter(\.$id ~~ audioIds)
      .filter(\.$id != systemAudioID)  // 过滤掉系统音频
      .filter(\.$isDeleted == false)
      .with(\.$album)  // 加载专辑信息
      .all()

    let foundAudioIds = Set(audios.compactMap { $0.id })
    let missingAudioIds = Set(audioIds).subtracting(foundAudioIds)
    if !missingAudioIds.isEmpty {

      // 可选：检查这些ID是否存在但被标记为删除
      let deletedAudios = try await Audio.query(on: req.db)
        .filter(\.$id ~~ Array(missingAudioIds))
        .filter(\.$isDeleted == true)
        .all()
      if !deletedAudios.isEmpty {
      }
    }
    // 查询这些音频的播放历史
    let histories = try await History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$audio.$id ~~ audioIds)
      .all()
    // 创建查找字典
    let historiesDict = histories.reduce(into: [UUID: History]()) { dict, history in
      let audioID = history.$audio.id
      dict[audioID] = history
    }

    // 构建响应
    let responses = audios.map { audio -> HistoryResponse in
      let history = historiesDict[audio.id!]

      return HistoryResponse(
        id: history?.id,
        progress: history?.progress ?? 0,
        duration: audio.duration,
        isFinished: history?.isFinished ?? false,
        lastListenedAt: history?.lastListenedAt,
        createdAt: history?.createdAt,
        listenedDuration: history?.listenedDuration ?? 0,
        deleteStatus: history?.deleteStatus ?? .normal,
        deletedAt: history?.deletedAt,
        listenStatus: history?.listenStatus ?? .inProgress,
        completedCount: history?.completedCount ?? 0,
        audio: AudioSearchResponse(
          id: audio.id,
          title: audio.title,
          description: audio.description,
          duration: audio.duration,
          cover: audio.cover,
          author: audio.author,
          url: audio.url,
          size: audio.size,
          albumID: audio.album.id,
          albumTitle: audio.album.title,
          albumCover: audio.album.cover,
          createdAt: audio.createdAt,
          updatedAt: audio.updatedAt
        )
      )
    }
    do {
      try await req.cache.set(
        responses,
        forKey: cacheKey,
        duration: CacheConfig.Duration.medium,
        type: .audio,
        on: req.redis
      )
    } catch {
    }

    return responses
  }

  @Sendable
  func create(req: Request) async throws -> BasicResponse {
    _ = req.logger
    let createRequest = try req.content.decode(Audio.Create.self)

    return try await req.db.transaction { db in
      // 检查音频是否已存在
      if try await Audio.query(on: db)
        .filter(\.$url == createRequest.url)
        .first() != nil
      {
        throw Abort(.conflict, reason: "该音频已存在")
      }

      // 创建音频
      let audio = Audio(
        title: createRequest.title,
        description: createRequest.description,
        duration: createRequest.duration,
        cover: createRequest.cover,
        author: createRequest.author,
        url: createRequest.url,
        size: createRequest.size,
        isDeleted: false,
        createdAt: Date(),
        updatedAt: Date(),
        albumID: createRequest.albumID
      )

      try await audio.save(on: db)
      let audioID = try audio.requireID()

      // 更新专辑的 audioCount
      guard let album = try await Album.find(createRequest.albumID, on: db) else {
        throw Abort(.notFound, reason: "未找到对应的专辑")
      }
      album.audioCount += 1
      try await album.save(on: db)

      // 使用 Set 去重
      var uniqueTranscripts = Set<TranscriptKey>()
      var transcriptsToCreate: [Transcript] = []

      // 检查重复并准备数据
      for transcriptDTO in createRequest.transcripts {
        let key = TranscriptKey(audioID: audioID, startTime: transcriptDTO.startTime)
        if uniqueTranscripts.insert(key).inserted {
          let transcript = Transcript(
            sentence: PunctuationConverter.convertChineseToArabicPunctuation(
              transcriptDTO.sentence),
            translation: transcriptDTO.translation,
            startTime: transcriptDTO.startTime,
            endTime: transcriptDTO.endTime,
            wordCount: ArabicWordCounter.countWords(transcriptDTO.sentence),
            isDeleted: false,
            createdAt: Date(),
            updatedAt: Date(),
            audioID: audioID
          )
          transcriptsToCreate.append(transcript)
        } else {
        }
      }

      // 批量保存，使用较小的批次
      let batchSize = 30
      try await withThrowingTaskGroup(of: Void.self) { group in
        for batch in transcriptsToCreate.chunked(into: batchSize) {
          group.addTask {
            for transcript in batch {
              if try await Transcript.query(on: db)
                .filter(\Transcript.$audio.$id == audioID)
                .filter(\Transcript.$startTime == transcript.startTime)
                .first() == nil
              {
                try await transcript.create(on: db)
              }
            }
          }
        }
        try await group.waitForAll()
      }
      
        // 计算并更新专辑的wordCount
      let totalWordCount = transcriptsToCreate.reduce(0) { $0 + $1.wordCount}
      album.wordCount += totalWordCount
      try await album.save(on: db)

      return BasicResponse(success: true, message: "音频创建成功", id: audioID)
    }
  }

  // 辅助结构体用于去重
  private struct TranscriptKey: Hashable {
    let audioID: UUID
    let startTime: Double

    func hash(into hasher: inout Hasher) {
      hasher.combine(audioID)
      hasher.combine(startTime)
    }
  }

  @Sendable
  func getAudiosByAlbum(req: Request) async throws -> [HistoryResponse] {
    let userID = try req.authenticatedUserID
    guard let albumID = req.parameters.get("albumID", as: UUID.self) else {
      throw Abort(.badRequest, reason: "Invalid album ID")
    }

    // 如果是系统专辑ID，直接返回空数组
    let systemAlbumID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!
    if albumID == systemAlbumID {
      return []
    }

    let cacheKey = CacheConfig.KeyBuilder.audiosByAlbum(albumID: albumID, userID: userID)

    // 从缓存获取
    if let cachedResponses = try await req.cache.get(
      forKey: cacheKey,
      as: [HistoryResponse].self,
      from: req.redis
    ) {
      return cachedResponses
    }

    // 系统音频ID
    let systemAudioID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!

    // 查询该专辑的所有未删除音频，过滤掉系统音频
    let audios = try await Audio.query(on: req.db)
      .filter(\.$album.$id == albumID)
      .filter(\.$id != systemAudioID)  // 过滤掉系统音频
      .filter(\.$isDeleted == false)
      .sort(\.$createdAt, .ascending)
      .with(\.$album)  // 加载专辑信息
      .all()

    // 获取这些音频的播放历史
    let histories = try await History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$audio.$id ~~ audios.compactMap { $0.id })
      .all()
    // 创建历史记录查找字典
    let historiesDict = histories.reduce(into: [UUID: History]()) { dict, history in
      let audioID = history.$audio.id
      dict[audioID] = history
    }

    // 构建响应
    let responses = audios.map { audio -> HistoryResponse in
      let history = historiesDict[audio.id!]

      return HistoryResponse(
        id: history?.id,
        progress: history?.progress ?? 0,
        duration: audio.duration,
        isFinished: history?.isFinished ?? false,
        lastListenedAt: history?.lastListenedAt,
        createdAt: history?.createdAt,
        listenedDuration: history?.listenedDuration ?? 0,
        deleteStatus: history?.deleteStatus ?? .normal,
        deletedAt: history?.deletedAt,
        listenStatus: history?.listenStatus ?? .inProgress,
        completedCount: history?.completedCount ?? 0,
        audio: AudioSearchResponse(
          id: audio.id,
          title: audio.title,
          description: audio.description,
          duration: audio.duration,
          cover: audio.cover,
          author: audio.author,
          url: audio.url,
          size: audio.size,
          albumID: audio.album.id,
          albumTitle: audio.album.title,
          albumCover: audio.album.cover,
          createdAt: audio.createdAt,
          updatedAt: audio.updatedAt
        )
      )
    }

    // 缓存响应
    do {
      try await req.cache.set(
        responses,
        forKey: cacheKey,
        duration: CacheConfig.Duration.medium,
        type: .audio,
        on: req.redis
      )
    } catch {
      // 处理缓存错误
    }

    return responses
  }

  // New endpoint to get audio and album covers
  @Sendable
  func getAudioCovers(req: Request) async throws -> AudioCoversResponse {
    // Get audioID from parameters
    guard let audioID = req.parameters.get("audioID", as: UUID.self) else {
      throw Abort(.badRequest, reason: "Invalid audio ID")
    }

    // 检查是否是系统音频ID
    let systemAudioID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!
    if audioID == systemAudioID {
      throw Abort(.notFound, reason: "Audio not found or has been deleted")
    }

    // Fetch audio with its album
    guard
      let audio = try await Audio.query(on: req.db)
        .filter(\.$id == audioID)
        .filter(\.$isDeleted == false)
        .with(\.$album)
        .first()
    else {
      throw Abort(.notFound, reason: "Audio not found or has been deleted")
    }

    // Construct and return the response
    return AudioCoversResponse(
      audioID: audio.id!,
      audioCover: audio.cover,
      albumID: audio.album.id!,
      albumCover: audio.album.cover
    )
  }
}

// Response type for audio covers endpoint
struct AudioCoversResponse: Content {
  let audioID: UUID
  let audioCover: String?
  let albumID: UUID
  let albumCover: String?
}

// Response type for audio details public endpoint
struct AudioPublicResponse: Content {
  let id: UUID
  let title: String
  let description: String?
  let duration: Double
  let cover: String?
  let author: String?
  let url: String
  let size: Double
  let albumID: UUID
  let albumTitle: String
  let albumCover: String?
}
